package com.andyliu.aspect;

import com.andyliu.interceptor.BusinessType;
import com.andyliu.log.Log;
import com.andyliu.system.entity.Dept;
import com.andyliu.system.entity.OperLog;
import com.andyliu.system.entity.User;
import com.andyliu.system.service.IDeptService;
import com.andyliu.system.service.IOperLogService;
import com.andyliu.system.service.IUserService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerMapping;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * @packageName: com.andyliu.aspect
 * @user: andyliu
 * @date: 2021/9/18 9:19
 * @email 115176513@qq.com
 * @description: TODO
 */
@Component
@Slf4j
@Aspect
public class LogAspect {
    @Autowired
    private IUserService userService;

    @Autowired
    private IDeptService deptService;

    @Autowired
    private IOperLogService operLogService;


    @Pointcut("@annotation(com.andyliu.log.Log)")
    public void pointCut(){

    }

    @AfterThrowing(pointcut = "pointCut()",throwing = "ex")
    public void exLog(JoinPoint joinPoint,Throwable ex){
        Log log = getLogAnnotation(joinPoint);
        if(log==null)return;
        // 获取当前登录用户信息
        Subject subject = SecurityUtils.getSubject();
        Object userName = subject.getPrincipal();
        User user = userService.getUserByLoginName(userName.toString());
        Long deptId = user.getDeptId();
        Dept dept = deptService.getById(deptId);
        String deptName = dept.getDeptName();// 部门名称
        String moduleName = log.moduleName();
        BusinessType businessType = log.businessType();
        // 方法名
        String methodName = joinPoint.getSignature().getName();
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)attributes;
        // 请求方式
        String requestMethod = servletRequestAttributes.getRequest().getMethod();
        String requestURI = servletRequestAttributes.getRequest().getRequestURI();// uri
        String ip = servletRequestAttributes.getRequest().getRemoteAddr();
        boolean status = log.isSaveParam();
        // 请求参数
        String params = getRequestParam(joinPoint,log,requestMethod,servletRequestAttributes);

        OperLog operLog = new OperLog();
        operLog.setBusinessType(businessType.ordinal());
        operLog.setDeptName(deptName);
        operLog.setMethod(methodName);
        operLog.setOperUrl(requestURI);
        operLog.setOperIp(ip);
        operLog.setOperName(moduleName);
        operLog.setOperParam(params);
        operLog.setOperTime(LocalDateTime.now());
        ObjectMapper mapper = new ObjectMapper();
        operLog.setErrorMsg(ex.getMessage());
        operLog.setStatus(1);
        operLog.setRequestMethod(requestMethod);
        operLogService.save(operLog);
    }


    @AfterReturning(returning = "data",pointcut = "pointCut()")
    public void afterLog(JoinPoint joinPoint,Object data) throws JsonProcessingException {
        Log log = getLogAnnotation(joinPoint);
        if(log==null)return;
        // 获取当前登录用户信息
        Subject subject = SecurityUtils.getSubject();
        Object userName = subject.getPrincipal();
        if(userName!=null) {
            User user = userService.getUserByLoginName(userName.toString());
            Long deptId = user.getDeptId();
            Dept dept = deptService.getById(deptId);
            String deptName = dept.getDeptName();// 部门名称
            String moduleName = log.moduleName();
            BusinessType businessType = log.businessType();
            // 方法名
            String methodName = joinPoint.getSignature().getName();
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) attributes;
            // 请求方式
            String requestMethod = servletRequestAttributes.getRequest().getMethod();
            String requestURI = servletRequestAttributes.getRequest().getRequestURI();// uri
            String ip = servletRequestAttributes.getRequest().getRemoteAddr();
            boolean status = log.isSaveParam();
            // 请求参数
            String params = getRequestParam(joinPoint, log, requestMethod, servletRequestAttributes);

            OperLog operLog = new OperLog();
            operLog.setBusinessType(businessType.ordinal());
            operLog.setDeptName(deptName);
            operLog.setMethod(methodName);
            operLog.setOperUrl(requestURI);
            operLog.setOperIp(ip);
            operLog.setOperName(moduleName);
            operLog.setOperParam(params);
            operLog.setOperTime(LocalDateTime.now());
            operLog.setRequestMethod(requestMethod);
            ObjectMapper mapper = new ObjectMapper();
            operLog.setJsonResult(mapper.writeValueAsString(data));
            operLog.setStatus(0);
            operLogService.save(operLog);
        }

    }

    public String getRequestParam(JoinPoint joinPoint, Log log, String requestMethod, ServletRequestAttributes attributes){

        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
        {
            String params = argsArrayToString(joinPoint.getArgs());
            return params;
        }
        else
        {
            Map<?, ?> paramsMap = (Map<?, ?>) attributes.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            return paramsMap.toString();
        }
    }

    private String argsArrayToString(Object[] args) {
        String params="";

        if(args!=null){
            for(int i=0;i<args.length;i++){
                Object obj = args[i];
                if(obj!=null){
                    ObjectMapper mapper = new ObjectMapper();
                    try {
                        String jsonStr  = mapper.writeValueAsString(obj);
                        params+= jsonStr;
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
        return params;
    }

    public Log getLogAnnotation(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        Method method = methodSignature.getMethod();
        if(method != null){
            Log annotation = method.getAnnotation(Log.class);
            return annotation;
        }
        return null;
    }

}
